{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cython：Cython 语法，调用其他C库"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cython 语法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cdef 关键词"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`cdef` 定义 `C` 类型变量。 \n",
    "\n",
    "可以定义局部变量：\n",
    "\n",
    "```cython\n",
    "def fib(int n):\n",
    "    cdef int a,b,i\n",
    "    ...\n",
    "```\n",
    "\n",
    "定义函数返回值：\n",
    "\n",
    "```cython\n",
    "cdef float distance(float *x, float *y, int n):\n",
    "    cdef:\n",
    "        int i\n",
    "        float d = 0.0\n",
    "    for i in range(n):\n",
    "        d += (x[i] - y[i]) ** 2\n",
    "    return d\n",
    "```\n",
    "\n",
    "定义函数：\n",
    "```cython\n",
    "cdef class Particle(object):\n",
    "    cdef float psn[3], vel[3]\n",
    "    cdef int id\n",
    "```\n",
    "\n",
    "注意函数的参数不需要使用 cdef 的定义。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### def, cdef, cpdef 函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Cython` 一共有三种定义方式，`def, cdef, cpdef` 三种：\n",
    "\n",
    "- `def` - Python, Cython 都可以调用\n",
    "- `cdef` - 更快，只能 Cython 调用，可以使用指针\n",
    "- `cpdef` - Python, Cython 都可以调用，不能使用指针"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cimport"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from math import sin as pysin\n",
    "from numpy import sin as npsin"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%load_ext Cython"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从标准 `C` 语言库中调用模块，`cimport` 只能在 `Cython` 中使用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%cython\n",
    "from libc.math cimport sin\n",
    "from libc.stdlib cimport malloc, free"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### cimport 和 pxd 文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果想在多个文件中复用 `Cython` 代码，可以定义一个 `.pxd` 文件（相当于头文件 `.h`）定义方法，这个文件对应于一个 `.pyx` 文件（相当于源文件 `.c`），然后在其他的文件中使用 `cimport` 导入：\n",
    "\n",
    "`fib.pxd, fib.pyx` 文件存在，那么可以这样调用：\n",
    "```cython\n",
    "from fib cimport fib\n",
    "```\n",
    "\n",
    "还可以调用 `C++` 标准库和 `Numpy C Api` 中的文件：\n",
    "```cython\n",
    "from libcpp.vector cimport vector\n",
    "cimport numpy as cnp\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 调用其他C库"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从标准库 `string.h` 中调用 `strlen`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing len_extern.pyx\n"
     ]
    }
   ],
   "source": [
    "%%file len_extern.pyx\n",
    "cdef extern from \"string.h\":\n",
    "    int strlen(char *c)\n",
    "    \n",
    "def get_len(char *message):\n",
    "    return strlen(message)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不过 `Cython` 不会自动扫描导入的头文件，所以要使用的函数必须再声明一遍："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing setup_len_extern.py\n"
     ]
    }
   ],
   "source": [
    "%%file setup_len_extern.py\n",
    "from distutils.core import setup\n",
    "from distutils.extension import Extension\n",
    "from Cython.Distutils import build_ext\n",
    "\n",
    "setup(\n",
    "  ext_modules=[ Extension(\"len_extern\", [\"len_extern.pyx\"]) ],\n",
    "  cmdclass = {'build_ext': build_ext}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "编译："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "running build_ext\n",
      "cythoning len_extern.pyx to len_extern.c\n",
      "building 'len_extern' extension\n",
      "creating build\n",
      "creating build\\temp.win-amd64-2.7\n",
      "creating build\\temp.win-amd64-2.7\\Release\n",
      "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\\Miniconda\\include -IC:\\Miniconda\\PC -c len_extern.c -o build\\temp.win-amd64-2.7\\Release\\len_extern.o\n",
      "writing build\\temp.win-amd64-2.7\\Release\\len_extern.def\n",
      "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -shared -s build\\temp.win-amd64-2.7\\Release\\len_extern.o build\\temp.win-amd64-2.7\\Release\\len_extern.def -LC:\\Miniconda\\libs -LC:\\Miniconda\\PCbuild\\amd64 -lpython27 -lmsvcr90 -o \"C:\\Users\\Jin\\Documents\\Git\\python-tutorial\\07. interfacing with other languages\\len_extern.pyd\"\n"
     ]
    }
   ],
   "source": [
    "!python setup_len_extern.py build_ext --inplace"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从 `Python` 中调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import len_extern"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用这个模块后，并不能直接使用 `strlen` 函数，可以看到，这个模块中并没有 `strlen` 这个函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__builtins__',\n",
       " '__doc__',\n",
       " '__file__',\n",
       " '__name__',\n",
       " '__package__',\n",
       " '__test__',\n",
       " 'get_len']"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(len_extern)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不过可以调用 `get_len` 函数： "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len_extern.get_len('hello')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因为调用的是 `C` 函数，所以函数的表现与 `C` 语言的用法一致，例如 `C` 语言以 `\\0` 为字符串的结束符，所以会出现这样的情况："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len_extern.get_len('hello\\0world!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了对已有的 `C` 函数进行调用，还可以对已有的 `C` 结构体进行调用和修改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing time_extern.pyx\n"
     ]
    }
   ],
   "source": [
    "%%file time_extern.pyx\n",
    "cdef extern from \"time.h\":\n",
    "\n",
    "    struct tm:\n",
    "        int tm_mday\n",
    "        int tm_mon\n",
    "        int tm_year\n",
    "\n",
    "    ctypedef long time_t\n",
    "    tm* localtime(time_t *timer)\n",
    "    time_t time(time_t *tloc)\n",
    "\n",
    "def get_date():\n",
    "    \"\"\"Return a tuple with the current day, month and year.\"\"\"\n",
    "    cdef time_t t\n",
    "    cdef tm* ts\n",
    "    t = time(NULL)\n",
    "    ts = localtime(&t)\n",
    "    return ts.tm_mday, ts.tm_mon + 1, ts.tm_year + 1900"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们只使用 `tm` 结构体的年月日信息，所以只声明了要用了三个属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing setup_time_extern.py\n"
     ]
    }
   ],
   "source": [
    "%%file setup_time_extern.py\n",
    "from distutils.core import setup\n",
    "from distutils.extension import Extension\n",
    "from Cython.Distutils import build_ext\n",
    "\n",
    "setup(\n",
    "  ext_modules=[ Extension(\"time_extern\", [\"time_extern.pyx\"]) ],\n",
    "  cmdclass = {'build_ext': build_ext}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "编译："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "running build_ext\n",
      "cythoning time_extern.pyx to time_extern.c\n",
      "building 'time_extern' extension\n",
      "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\\Miniconda\\include -IC:\\Miniconda\\PC -c time_extern.c -o build\\temp.win-amd64-2.7\\Release\\time_extern.o\n",
      "writing build\\temp.win-amd64-2.7\\Release\\time_extern.def\n",
      "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -shared -s build\\temp.win-amd64-2.7\\Release\\time_extern.o build\\temp.win-amd64-2.7\\Release\\time_extern.def -LC:\\Miniconda\\libs -LC:\\Miniconda\\PCbuild\\amd64 -lpython27 -lmsvcr90 -o \"C:\\Users\\Jin\\Documents\\Git\\python-tutorial\\07. interfacing with other languages\\time_extern.pyd\"\n"
     ]
    }
   ],
   "source": [
    "!python setup_time_extern.py build_ext --inplace"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "测试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(19, 9, 2015)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import time_extern\n",
    "\n",
    "time_extern.get_date()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "清理文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import zipfile\n",
    "\n",
    "f = zipfile.ZipFile('07-04-extern.zip','w',zipfile.ZIP_DEFLATED)\n",
    "\n",
    "names = ['setup_len_extern.py',\n",
    "         'len_extern.pyx',\n",
    "         'setup_time_extern.py',\n",
    "         'time_extern.pyx']\n",
    "for name in names:\n",
    "    f.write(name)\n",
    "\n",
    "f.close()\n",
    "\n",
    "!rm -f setup*.*\n",
    "!rm -f len_extern.*\n",
    "!rm -f time_extern.*\n",
    "!rm -rf build"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
